﻿/*
 * timer_2ms.c
 *
 *  Created on: 2019年4月26日
 *      Author: Jinming Chen
 */

#include <string.h>
#include "timer_2ms.h"
#include "array2.h"
#include "cmsis_os2.h"
#include "FreeRTOS.h"
#include "task_info.h"

typedef struct _TimerNode
{
	char *name;			//名称/标志
	uint16_t cnt;     	//计时器，步进2ms
	uint16_t time;		//定时时间
	uint8_t running;		//当前节点中断是否在正在被调用
	uint8_t avaliable;		//是否可用
	void (*fun)(void *);	//超时执行函数
	void *param;				//参数
	struct _TimerNode *next;
}TimerNode;

typedef struct
{
	uint8_t free;			//是否需要释放参数内存
	void (*fun)(void *);	//待注销节点的回调函数，即节点ID
}unregisterNode;

TimerNode *g_ptHeader = NULL;
static Array2 gs_tUnregisterNode;

osThreadId_t g_pTimer2MsTaskHand = NULL;
osSemaphoreId_t g_pTimer2MsTaskSemHandle = NULL;

/************************************************************************************************
 * @brief 	注册定时操作到2ms硬件定时器。
 * @param	name:定时操作标志
 * @param	timer:定时时间，定时总时长 = timer * 2  ms
 * @param   function:定时回调函数
 * @param	param:调函函数参数
 * @retval	注册是否成功
 ************************************************************************************************/
TIMER_ERR timer_reg_fun(const char *name, uint16_t timer, void (*function)(void *), void *param)
{
	TimerNode *ptNode, *ptTemp;
//	uint8_t count = 0;

//	ptNode = g_ptHeader;
//	while(ptNode)
//	{
//		count++;
//		ptNode = ptNode->next;
//	}
//
//	SEGGER_RTT_printf(0,"time count=%d\n", count);
	if(g_ptHeader == NULL)		//如果还没有任何操作注册进来
	{
		ptTemp = g_ptHeader;
	}
	else
	{
		//如果队列内已有定时操作，那么找到队列尾，并将操作挂到队列尾
		ptTemp = g_ptHeader;
		if(ptTemp->fun == function)
			return TIMER_REG_ERR;

		while(ptTemp->next)
		{
			if(ptTemp->next->fun == function)		//重复,出错
			{
				return TIMER_REG_ERR;
			}
			else
			{
				ptTemp = ptTemp->next;
			}
		}
	}

	ptNode = (TimerNode *)pvPortMalloc(sizeof(TimerNode));
	if(ptNode == NULL)
		return TIMER_REG_ERR;

	if(name)
	{
		ptNode->name = pvPortMalloc(strlen(name));
		if(ptNode->name == NULL)
			return TIMER_REG_ERR;
		else
			memcpy(ptNode->name, name, strlen(name));
	}
	else
	{
		ptNode->name = NULL;
	}

	ptNode->cnt = 0;
	ptNode->time = timer;
	ptNode->fun = function;
	ptNode->param = param;
	ptNode->running = 0;
	ptNode->next = NULL;
	ptNode->avaliable = 1;

	if(g_ptHeader == NULL)
	{
		g_ptHeader = ptNode;
	}
	else
	{
		ptTemp->next = ptNode;
	}

	return TIMER_REG_OK;
}

/************************************************************************************************
 * @brief 	删除指定的定时操作
 * @param   function:定时回调函数，此处用于标志定时操作
 * @retval	删除是否成功
 ************************************************************************************************/
TIMER_ERR timer_unreg_fun(void (*function)(void *))
{
	TimerNode *ptTemp, *ptNode;
	unregisterNode tUnregisterNode;

	//SEGGER_RTT_printf(0,"function count=%d\n", timer_search(function));
	if(g_ptHeader == NULL)
		return TIMER_NO_FIND;

	if(g_ptHeader->fun == function)	//如果需要删除队头
	{
		if(g_ptHeader->running)		//正在被回调，先把回调ID存到缓冲区，回调完毕后再注销
		{
			tUnregisterNode.free = 0;
			tUnregisterNode.fun = function;
			array2_put_lifo(&gs_tUnregisterNode, (uint8_t *)&tUnregisterNode, sizeof(unregisterNode));
			return TIMER_UNREG_OK;
		}

		ptTemp = g_ptHeader;
		g_ptHeader = g_ptHeader->next;
		if(ptTemp->name)
			vPortFree(ptTemp->name);
		ptTemp->next = NULL;
		vPortFree(ptTemp);

		return TIMER_UNREG_OK;
	}

	//根据回调函数找到对应的节点，并将该节点从队列删除
	ptNode = g_ptHeader;
	while(ptNode->next)
	{
		if(ptNode->next->fun == function)
		{
			if(ptNode->next->running)		//正在被回调，先把回调ID存到缓冲区，回调完毕后再注销
			{
				tUnregisterNode.free = 0;
				tUnregisterNode.fun = function;
				array2_put_lifo(&gs_tUnregisterNode, (uint8_t *)&tUnregisterNode, sizeof(unregisterNode));
				return TIMER_UNREG_OK;
			}

			ptTemp = ptNode->next;
			ptNode->next = ptNode->next->next;
			if(ptTemp->name)
				vPortFree(ptTemp->name);
			vPortFree(ptTemp);

			return TIMER_UNREG_OK;
		}
		else
		{
			ptNode = ptNode->next;
		}
	}

	//SEGGER_RTT_printf(0,"function count1=%d\n", timer_search(function));

	return TIMER_NO_FIND;
}

void timer_set_param(void (*function)(void *), void *param)
{
	TimerNode *ptNode = g_ptHeader;

	while(ptNode)
	{
		if(ptNode->fun == function)
		{
			ptNode->param = param;
			return;
		}
		ptNode = ptNode->next;
	}
}

/************************************************************************************************
 * @brief 	删除指定的定时操作,同时释放参数内存
 * @param   function:定时回调函数，此处用于标志定时操作
 * @retval	删除是否成功
 ************************************************************************************************/
TIMER_ERR timer_unreg_fun_with_free(void (*function)(void *))
{
	TimerNode *ptTemp, *ptNode;
	unregisterNode tUnregisterNode;

	if(g_ptHeader == NULL)
		return TIMER_NO_FIND;

	if(g_ptHeader->fun == function)	//如果需要删除队头
	{
		if(g_ptHeader->running)		//正在被回调，先把回调ID存到缓冲区，回调完毕后再注销
		{
			tUnregisterNode.free = 1;
			tUnregisterNode.fun = function;
			array2_put_lifo(&gs_tUnregisterNode, (uint8_t *)&tUnregisterNode, sizeof(unregisterNode));
			return TIMER_UNREG_OK;
		}

		ptTemp = g_ptHeader;
		g_ptHeader = g_ptHeader->next;
		if(ptTemp->name)
			vPortFree(ptTemp->name);
		if(ptTemp->param)
			vPortFree(ptTemp->param);
		ptTemp->next = NULL;
		vPortFree(ptTemp);

		return TIMER_UNREG_OK;
	}

	//根据回调函数找到对应的节点，并将该节点从队列删除
	ptNode = g_ptHeader;
	while(ptNode->next)
	{
		if(ptNode->next->fun == function)
		{
			if(ptNode->next->running)		//正在被回调，先把回调ID存到缓冲区，回调完毕后再注销
			{
				tUnregisterNode.free = 1;
				tUnregisterNode.fun = function;
				array2_put_lifo(&gs_tUnregisterNode, (uint8_t *)&tUnregisterNode, sizeof(unregisterNode));
				return TIMER_UNREG_OK;
			}

			ptTemp = ptNode->next;
			ptNode->next = ptNode->next->next;
			if(ptTemp->name)
				vPortFree(ptTemp->name);
			if(ptTemp->param)
				vPortFree(ptTemp->param);
			vPortFree(ptTemp);

			return TIMER_UNREG_OK;
		}
		else
		{
			ptNode = ptNode->next;
		}
	}

	return TIMER_NO_FIND;
}

/************************************************************************************************
 * @brief 	将指定定时器重新开始计时
 * @param	function:定时回调函数，此处用于标志定时操作
 * @retval  操作是否成功
 ************************************************************************************************/
TIMER_ERR timer_retime(void (*function)(void *))
{
	TimerNode *ptNode;

	if(g_ptHeader == NULL)
		return TIMER_NO_FIND;

	ptNode = g_ptHeader;
	while(ptNode)
	{
		if(ptNode->fun == function)
		{
			ptNode->cnt = 0;

			return TIMER_OPS_OK;
		}
		else
		{
			ptNode = ptNode->next;
		}
	}

	return TIMER_NO_FIND;
}

/************************************************************************************************
 * @brief 	搜索指定定时器的个数
 * @param	function:定时回调函数，此处用于标志定时操作
 * @retval  该定时器在定时器链表的节点数量
 ************************************************************************************************/
uint8_t timer_search_count(void (*function)(void *))
{
	TimerNode *ptNode = g_ptHeader;

	uint8_t count = 0;

	while(ptNode)
	{
		if(ptNode->fun == function)
		{
			count++;
			ptNode = ptNode->next;
		}
		else
		{
			ptNode = ptNode->next;
		}
	}

	return count;
}

/************************************************************************************************
 * @brief 	搜索指定定时器，并将在定时器链表上找到的第一个节点返回
 * @param	function:定时回调函数，此处用于标志定时操作
 * @retval  定时器节点
 ************************************************************************************************/
TimerNode *timer_search(void (*function)(void *))
{
	TimerNode *ptNode = g_ptHeader;

	while(ptNode)
	{
		if(ptNode->fun == function)
		{
			return ptNode;
		}
		ptNode = ptNode->next;
	}

	return NULL;
}

/************************************************************************************************
 * @brief 	暂停指定定时器
 * @param	function:定时回调函数，此处用于标志定时操作
 * @retval  无
 ************************************************************************************************/
TIMER_ERR timer_stop(void (*function)(void *))
{
	TimerNode *ptNode = timer_search(function);

	if(ptNode)
	{
		ptNode->avaliable = 0;
		return TIMER_OPS_OK;
	}

	return TIMER_NO_FIND;
}

/************************************************************************************************
 * @brief 	指定定时器开始定时
 * @param	function:定时回调函数，此处用于标志定时操作
 * @retval  无
 ************************************************************************************************/
TIMER_ERR timer_start(void (*function)(void *))
{
	TimerNode *ptNode = timer_search(function);

	if(ptNode)
	{
		ptNode->avaliable = 1;
		return TIMER_OPS_OK;
	}

	return TIMER_NO_FIND;
}

const osSemaphoreAttr_t tTimer2msSem = {
	.name = "Timer2msSem"
};

/************************************************************************************************
 * @brief 	定时队列处理，在硬件定时触发调用
 * @param
 * @retval
 ************************************************************************************************/
static void timer_task(void *argument)
{
	TimerNode *ptNode = g_ptHeader;
	unregisterNode tUnregisterNode;
	uint16_t nLen;
	osStatus_t eTimer2msSemaStatus;

//	osDelay(100);
	g_pTimer2MsTaskSemHandle = osSemaphoreNew(5, 0, &tTimer2msSem);
	while(1)
	{
		eTimer2msSemaStatus = osSemaphoreAcquire (g_pTimer2MsTaskSemHandle, portMAX_DELAY);
		if(eTimer2msSemaStatus != osOK)
			continue;

		ptNode = g_ptHeader;
		while(ptNode)
		{
			if(ptNode->avaliable)			//如果当前定时器可用
			{
				ptNode->cnt++;
				if(ptNode->cnt == ptNode->time)
				{
					ptNode->cnt = 0;
					ptNode->running = 1;
					ptNode->fun(ptNode->param);
					ptNode->running = 0;
				}
			}

			ptNode = ptNode->next;
		}

		nLen = array2_get_size(&gs_tUnregisterNode);
		while(nLen)
		{
			array2_post(&gs_tUnregisterNode, (uint8_t *)&tUnregisterNode, &nLen);
			if(tUnregisterNode.free)
			{
				timer_unreg_fun_with_free(tUnregisterNode.fun);
			}
			else
			{
				timer_unreg_fun(tUnregisterNode.fun);
			}
			nLen = array2_get_size(&gs_tUnregisterNode);
		}
//		osThreadSuspend(g_pTimer2MsTaskHand);
	}
}

/************************************************************************************************
 * @brief 	定时，在硬件定时中使用
 * @param
 * @retval
 ************************************************************************************************/
void timer_interrupt(void)
{
	osSemaphoreRelease(g_pTimer2MsTaskSemHandle);
}

#if	THREAD_STACK_CHECK
uint32_t timer2ms_get_remaining_stack_space(void)
{
	return osThreadGetStackSpace(g_pTimer2MsTaskHand);
}
#endif

void timer2ms_init(void)
{
	const osThreadAttr_t tTimer2msTask = {
		.name = TIMER_2MS_TASK_NAME,
		.priority = (osPriority_t) TIMER_2MS_TASK_PRIO,
		.stack_size = 1280,
	};

	array2_malloc(&gs_tUnregisterNode, 20, sizeof(unregisterNode));
	g_pTimer2MsTaskHand = osThreadNew(timer_task, NULL, &tTimer2msTask);

}
